﻿using Newtonsoft.Json;
using System.Collections;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Web;

namespace HttpClientLite.Extensions;

/// <summary>
///
/// </summary>
public static class HttpManagerExtensions
{
    /// <summary>
    /// HttpManager配置
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="configurationMethod"></param>
    /// <returns></returns>
    public static HttpManagerBase HttpManagerConfiguration(this HttpManagerBase httpManager, Action<HttpManagerBase> configurationMethod)
    {
        return httpManager.Configuration(new HttpManagerConfigurationMethod(configurationMethod));
    }

    /// <summary>
    /// HttpRequestMessage配置
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="configurationMethod"></param>
    /// <returns></returns>
    public static HttpManagerBase HttpClientConfiguration(this HttpManagerBase httpManager, Action<HttpClient> configurationMethod)
    {
        return httpManager.Configuration(new HttpClientConfigurationMethod(configurationMethod));
    }

    /// <summary>
    /// HttpRequestMessage配置
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="configurationMethod"></param>
    /// <returns></returns>
    public static HttpManagerBase RequestMessageConfiguration(this HttpManagerBase httpManager, Action<HttpRequestMessage> configurationMethod)
    {
        return httpManager.Configuration(new RequestMessageConfigurationMethod(configurationMethod));
    }

    /// <summary>
    /// HttpResponseMessage处理
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="handlerMethod"></param>
    /// <returns></returns>
    public static HttpManagerBase ResponseMessageHandler(this HttpManagerBase httpManager, Func<HttpManagerBase, HttpClient, HttpRequestMessage, CancellationToken?, Task<HttpResponseMessage>> handlerMethod)
    {
        return httpManager.Handler(new ResponseMessageHandlerMethod(handlerMethod));
    }

    /// <summary>
    /// HttpResponseMessage处理
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="handlerMethod"></param>
    /// <returns></returns>
    public static HttpManagerBase ResponseMessageHandler(this HttpManagerBase httpManager, Func<HttpRequestMessage, Task<HttpResponseMessage>> handlerMethod)
    {
        return httpManager.Handler(new ResponseMessageHandlerMethod((_, _, r, _) => handlerMethod(r)));
    }

    /// <summary>
    /// HttpManager异常处理
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="handlerMethod"></param>
    /// <returns></returns>
    public static HttpManagerBase HttpManagerExceptionHandler(this HttpManagerBase httpManager, Action<Exception, HttpResponseMessage?> handlerMethod)
    {
        return httpManager.Handler(new HttpManagerExceptionHandlerMethod(handlerMethod));
    }

    /// <summary>
    /// 异步 GET 方法发送
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> GetAsync(this HttpManagerBase httpManager,
                                                           HttpCompletionOption? completionOption = null,
                                                           CancellationToken? cancellationToken = null)
    {
        return await httpManager.SendAsync(new HttpRequestMessage(), completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 GET 方法发送
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="url"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> GetAsync(this HttpManagerBase httpManager,
                                                           string url,
                                                           HttpCompletionOption? completionOption = null,
                                                           CancellationToken? cancellationToken = null)
    {
        var request = new HttpRequestMessage(HttpMethod.Get, url);

        return await httpManager.SendAsync(request, completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 GET 方法发送
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="url"></param>
    /// <param name="param"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> GetAsync(this HttpManagerBase httpManager,
                                                           string url,
                                                           object param,
                                                           HttpCompletionOption? completionOption = null,
                                                           CancellationToken? cancellationToken = null)
    {
        var request = new HttpRequestMessage(HttpMethod.Get, $"{url}?{SerializeToUrlParams(param)}");

        return await httpManager.SendAsync(request, completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 POST 方法发送
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="url"></param>
    /// <param name="stringContent"></param>
    /// <param name="mediaType"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> PostAsync(this HttpManagerBase httpManager,
                                                            string url,
                                                            string stringContent,
                                                            string mediaType = "application/json",
                                                            HttpCompletionOption? completionOption = null,
                                                            CancellationToken? cancellationToken = null)
    {
        return await httpManager.SendAsync(GenerateRequest(HttpMethod.Post, url, stringContent: stringContent, mediaType: mediaType), completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 POST 方法发送
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="url"></param>
    /// <param name="body"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <param name="jsonSerializerSettings"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> PostAsync(this HttpManagerBase httpManager,
                                                            string url,
                                                            object body,
                                                            HttpCompletionOption? completionOption = null,
                                                            CancellationToken? cancellationToken = null,
                                                            JsonSerializerSettings? jsonSerializerSettings = null)
    {
        string stringContent = ObjectConverToJson(body, jsonSerializerSettings);

        return await httpManager.PostAsync(url, stringContent, completionOption: completionOption, cancellationToken: cancellationToken);
    }

    /// <summary>
    /// 异步 POST 方法发送
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="url"></param>
    /// <param name="formDataValues"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <param name="jsonSerializerSettings"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> PostAsync(this HttpManagerBase httpManager,
                                                            string url,
                                                            Dictionary<string, string> formDataValues,
                                                            HttpCompletionOption? completionOption = null,
                                                            CancellationToken? cancellationToken = null,
                                                            JsonSerializerSettings? jsonSerializerSettings = null)
    {
        return await httpManager.SendAsync(GenerateRequest(HttpMethod.Post, formDataValues, url), completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 PUT 方法发送
    /// </summary>
    public static async Task<HttpResponseMessage> PutAsync(this HttpManagerBase httpManager,
                                                           string url,
                                                           string stringContent,
                                                           string mediaType = "application/json",
                                                           HttpCompletionOption? completionOption = null,
                                                           CancellationToken? cancellationToken = null)
    {
        return await httpManager.SendAsync(GenerateRequest(HttpMethod.Put, url, stringContent: stringContent, mediaType: mediaType), completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 PUT 方法发送
    /// </summary>
    public static async Task<HttpResponseMessage> PutAsync(this HttpManagerBase httpManager,
                                                           string url,
                                                           object body,
                                                           HttpCompletionOption? completionOption = null,
                                                           CancellationToken? cancellationToken = null,
                                                           JsonSerializerSettings? jsonSerializerSettings = null)
    {
        string stringContent = ObjectConverToJson(body, jsonSerializerSettings);

        return await httpManager.PutAsync(url, stringContent, completionOption: completionOption, cancellationToken: cancellationToken);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="httpManager"></param>
    /// <param name="url"></param>
    /// <param name="formDataValues"></param>
    /// <param name="completionOption"></param>
    /// <param name="cancellationToken"></param>
    /// <param name="jsonSerializerSettings"></param>
    /// <returns></returns>
    public static async Task<HttpResponseMessage> PutAsync(this HttpManagerBase httpManager,
                                                            string url,
                                                            Dictionary<string, string> formDataValues,
                                                            HttpCompletionOption? completionOption = null,
                                                            CancellationToken? cancellationToken = null,
                                                            JsonSerializerSettings? jsonSerializerSettings = null)
    {
        return await httpManager.SendAsync(GenerateRequest(HttpMethod.Put, formDataValues, url), completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 DELETE 方法发送
    /// </summary>
    public static async Task<HttpResponseMessage> DeleteAsync(this HttpManagerBase httpManager,
                                                              string url,
                                                              HttpCompletionOption? completionOption = null,
                                                              CancellationToken? cancellationToken = null)
    {
        var request = new HttpRequestMessage(HttpMethod.Delete, url);
        return await httpManager.SendAsync(request, completionOption, cancellationToken);
    }

    /// <summary>
    /// 异步 DELETE 方法发送（带参数）
    /// </summary>
    public static async Task<HttpResponseMessage> DeleteAsync(this HttpManagerBase httpManager,
                                                              string url,
                                                              object param,
                                                              HttpCompletionOption? completionOption = null,
                                                              CancellationToken? cancellationToken = null)
    {
        var request = new HttpRequestMessage(HttpMethod.Delete, $"{url}?{SerializeToUrlParams(param)}");
        return await httpManager.SendAsync(request, completionOption, cancellationToken);
    }

    private static HttpRequestMessage GenerateRequest(HttpMethod method,
                                                      string? url = null,
                                                      object? param = null,
                                                      string? stringContent = null,
                                                      string? mediaType = null)
    {
        if (!string.IsNullOrWhiteSpace(url) && param is not null)
        {
            url = $"{url}?{SerializeToUrlParams(param)}";
        }

        var request = new HttpRequestMessage(method, url);

        if (!string.IsNullOrWhiteSpace(stringContent))
        {
            var content = new StringContent(stringContent, Encoding.UTF8);

            if (!string.IsNullOrWhiteSpace(mediaType))
            {
                content.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
            }

            request.Content = content;
        }

        return request;
    }

    private static HttpRequestMessage GenerateRequest(HttpMethod method,
                                                      Dictionary<string, string> formDataValues,
                                                      string? url = null,
                                                      object? param = null)
    {
        if (!string.IsNullOrWhiteSpace(url) && param is not null)
        {
            url = $"{url}?{SerializeToUrlParams(param)}";
        }

        var request = new HttpRequestMessage(method, url);

        var content = new MultipartFormDataContent();

        foreach (var value in formDataValues)
        {
            content.AddParameter(value.Key, value.Value);
        }

        request.Content = content;

        return request;
    }

    /// <summary>
    /// 将对象序列化为url参数
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="prefix"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    private static string SerializeToUrlParams(object obj, string prefix = "")
    {
#if NETCOREAPP
        ArgumentNullException.ThrowIfNull(obj, nameof(obj));
#else
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));
#endif
        var keyValuePairs = new List<string>();

        foreach (var property in obj.GetType().GetProperties())
        {
            var value = property.GetValue(obj);
            string key = string.IsNullOrEmpty(prefix)
                ? property.Name
                : $"{prefix}[{property.Name}]";

            if (value == null)
                continue;

            if (value is string || value.GetType().IsPrimitive) // 基础类型
            {
                keyValuePairs.Add($"{HttpUtility.UrlEncode(key)}={HttpUtility.UrlEncode(value.ToString())}");
            }
            else if (value is IEnumerable collection) // 集合类型
            {
                foreach (var item in collection)
                {
                    keyValuePairs.Add($"{HttpUtility.UrlEncode(key)}={HttpUtility.UrlEncode(item.ToString())}");
                }
            }
            else // 复杂对象
            {
                keyValuePairs.Add(SerializeToUrlParams(value, key));
            }
        }

        return string.Join("&", keyValuePairs);
    }

    private static string ObjectConverToJson(object obj, JsonSerializerSettings? jsonSerializerSettings = null)
    {
        string json = string.Empty;

        if (obj is not null)
        {
            if (obj is string)
            {
                json = obj.ToString()!;
            }
            else
            {
                if (jsonSerializerSettings is null)
                    json = ObjectJsonHelper.Serialize(obj);
                else
                    json = JsonConvert.SerializeObject(obj, jsonSerializerSettings);
            }
        }

        return json;
    }
}